#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

// 样例测试，测试服务器端出现CLOSE_WAIT状态
// 即客户端发送断开链接，服务器不断开链接也就是不close。此时就会维护在此状态
void User(char* str)
{
    std::cout << "./" << str << " port\n";
}

int main(int arc, char* argv[])
{
    if (arc != 2)
    {
        User(argv[0]);
    }

    int serverSock = socket(AF_INET, SOCK_STREAM, 0);  // 0自动识别，使用TCP协议进行通信
    // 对监听套接字进行处理，让服务器连接时主动退出，能够立即启动
    int opt = 1;  // 表示打开
    setsockopt(serverSock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);  // 在绑定前设置好
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[1]));
    inet_aton("0.0.0.0", &addr.sin_addr);
    if (bind(serverSock, (sockaddr*)&addr, sizeof addr) < 0)
    {
        std::cerr << "bind error: " << errno << "-" << strerror(errno) << std::endl;
        exit(1);
    }

    // TCP初始化绑定完后需要设置监听套接字处于监听状态
    if (0 > listen(serverSock, 20))  // 限定等待数列为20个
    {
        std::cerr << "listen error: " << errno << "-" << strerror(errno) << std::endl;
        exit(1);
    }

    // 连接逻辑处理 常驻进程
    signal(SIGCHLD, SIG_IGN);  // 子进程退出返回此信号，设置为用户默认，这样就不会产生僵尸进程
    while(true)
    {
        // 首先先建立连接
        struct sockaddr_in client_addr;
        memset(&client_addr, 0, sizeof(client_addr));
        socklen_t addr_len = sizeof(client_addr);
        int sock = accept(serverSock, (sockaddr*)&client_addr, &addr_len);
        // 网络转本地字节序
        uint16_t clien_port = ntohs(client_addr.sin_port);
        char* client_ip = inet_ntoa(client_addr.sin_addr);

        if (sock < 0) continue;

        if (fork() == 0)
        {
            // 子进程
            close(serverSock);  // 关闭监听套接字，它不需要
            char buffer[1024];
            while (true)
            {
                ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);  //阻塞等待接受
                if (n > 0)
                {
                    buffer[n] = '\0';
                    std::cout << "[" << client_ip << ": " << clien_port << "]# " << buffer << std::endl; 
                }
                else if (n == 0)
                {
                    // 对方断开连接了
                    // close(sock);  // 测试CLOSE_WAIT状态
                    // break;  // 测试此状态此进程也不可退出，因为进程退出也会关闭文件描述符的
                }
                else
                {
                    // n < 0 出现错误
                    close(sock);
                    std::cerr << "recv error: " << errno << "-" << strerror(errno) << std::endl;
                    break;
                }
            }
        }
        // 父进程
        close(sock);  // 关闭服务套接字，父进程不需要
    }

    return 0;
}